package com.seari.custody.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.seari.custody.bean.Meeting;
import com.seari.custody.bean.Message;
import com.seari.custody.bean.WsPushDTO;
import com.seari.custody.config.CaffeineConfig;
import com.seari.custody.util.JwtTokenUtils;
import com.seari.custody.enums.MeetingType;
import com.seari.custody.service.MeetingService;
import com.seari.custody.service.MessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author lxv1
 * @Description: WebSocket服务端代码，包含接收消息，推送消息等接口
 * userName是接收的参数
 */
@Slf4j
@Component
@ServerEndpoint(value = "/ws/{token}")
public class WebsocketController {


    /**
     * 存储系统的用户信息
     */
    private static final ConcurrentHashMap<Session, String> map = new ConcurrentHashMap<>();
    public static final String MESSAGE_NOTIFY = "message";
    public static final String MEETING_NOTIFY = "meeting";


    /**
     * 判断是否在线并推送
     *
     * @param message
     */
    public static void onlinePush(Message message) {
        log.info("realtime push message to " + message.getRecvUserId());
        map.forEach((k, v) -> {
            if (Objects.equals(v, message.getRecvUserId())) {
                sendMessage(k, JSON.toJSONString(WsPushDTO.builder().type(MESSAGE_NOTIFY).data(Lists.newArrayList(message)).build()));
                pushMeeting(v, k);
            }
        });

    }

    /**
     * @param token
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("token") String token, Session session) {
        String userNumber = JwtTokenUtils.getUserNumber(token);
        map.put(session, userNumber);
        log.info("OnOpen()方法被执行...");
        log.info("websocket连接建立成功...");

        connectPush(userNumber, session);
    }


    public static void loopPush() {
        log.info("定时刷新会议通知");
        map.forEach((k, v) -> {
            pushMeeting(v, k);
        });
    }

    static void connectPush(String userNumber, Session session) {
        pushMessage(userNumber, session);
        pushMeeting(userNumber, session);
    }

    public static void pushMessage(String userNumber, Session session) {
        /**
         * 推送消息通知
         */
        log.info(DateUtil.now() + " push message to " + userNumber);
        try {
            List<Message> messages = SpringUtil.getBean(MessageService.class).statisticsUnReadMessageByUserNumber(userNumber);
            if (CollectionUtil.isEmpty(messages)) {
                return;
            }

            WsPushDTO build = WsPushDTO.builder().type(MESSAGE_NOTIFY).data(messages).build();
            sendMessage(session, JSON.toJSONString(build));
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    public static void pushMeeting(String userNumber, Session session) {
        /**
         * 会议即将开始通知
         */
        try {
            List<Meeting> meetings = SpringUtil.getBean(MeetingService.class).todayMeetings(userNumber);
            if (CollectionUtil.isEmpty(meetings)) {
                return;
            }
            Date now = new Date();
            Cache cache = SpringUtil.getBean(CacheManager.class).getCache(CaffeineConfig.COMMON);
            for (Meeting meeting : meetings) {
                Date startTime = meeting.getStartTime();
                Date before30 = DateUtil.offsetMinute(startTime, -30);
                Date before15 = DateUtil.offsetMinute(startTime, -15);
                String cacheKey = meeting.getId() + userNumber;

                String message = "";
                if (DateUtil.isIn(now, before30, before15)) {
                    //保证只通知一次
                    String s = "30";
                    String s1 = cacheKey + s;
                    if (Objects.requireNonNull(cache).get(s1) == null) {
                        message = s;
                        cache.put(s1, "");
                    }
                }
                if (DateUtil.isIn(now, before15, startTime)) {
                    String s = "15";
                    String s1 = cacheKey + s;
                    if (Objects.requireNonNull(cache).get(s1) == null) {
                        message = s;
                        cache.put(s1, "");
                    }
                }
                if (Strings.isNullOrEmpty(message)) {
                    continue;
                }

                WsPushDTO build = WsPushDTO.builder().type(MEETING_NOTIFY).data("会议提醒：您于"+ DateUtil.format(meeting.getStartTime(), "yyyy年MM月dd日HH时mm分")+"有"+ MeetingType.getDesc(meeting.getType())+"：" + meeting.getMeetingHeadline() + ",还有" + DateUtil.between(startTime, now, DateUnit.MINUTE,true) + "分钟即将召开"
                ).build();
                String string = JSON.toJSONString(build);
                sendMessage(session, string);
            }

        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 连接关闭的方法
     */
    @OnClose
    public void onClose(Session session) {
        map.remove(session);
        log.info("OnClose()方法被执行...");
        log.info("websocket连接已经关闭...");
    }

    /**
     * 接收消息的方法
     *
     * @param msg
     * @param session
     * @throws InterruptedException
     */
    @OnMessage
    public void onMessage(String msg, Session session) throws InterruptedException {
        log.info("已从客户端接收消息：" + msg);

        log.info("向客户端发送数据完毕...");
    }

    /**
     * 出错的方法，注意参数不能错
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        map.remove(session);
        log.info(error.getMessage());
    }

    /**
     * 推送数据的方法
     *
     * @param session map里存的登录信息
     * @param message 推送数据
     */
    public static void sendMessage(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            log.error(e.getMessage() + "失败消息[" + message + "]", e);
        }
    }


}
